#include<stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/msg.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include "My_sig.h"


struct Msg_sig{
    long type;
    int signal;
};
struct User{
    long type;
    char username[32];
    char password[32];
};
struct Contact{
    long type;
    char name[16];
    char sex[8];
    int age;
    char telephone[16];
    char address[32];
};
//后台进程

//定义存储用户的线性表
struct Seq_users{
    struct User users[1024];
    int n;
};


//定义存储联系人的线性表
struct Seq_contacts{
    struct Contact contacts[1024];
    int n;
};

//响应一些指令
int handle_sigs(int msgid,void* data,struct Seq_contacts* seq){
    struct Msg_sig* sig = (struct Msg_sig*)data;
    if(sig->signal == -1){
        //指令-1:退出系统
        return -1;
    }

    else if(sig->signal == 1){
        //指令1:发送通讯录的所有联系人信息
        printf("%d\n",seq->n);
        for(int i=0;i<(seq->n);i++){
            if(seq->contacts[i].name[0] == '\0'){
                //表示该联系人已被删除
                continue;
            }
            seq->contacts[i].type = RECEIVE_CONTACT;
            msgsnd(msgid,&seq->contacts[i],sizeof(struct Contact)-sizeof(long),0);
        }
        //如果通讯录没有联系人或者联系人都发送完毕,以此结束
        struct Contact contact = {0};
        contact.age = -1;
        contact.type = RECEIVE_CONTACT;
        msgsnd(msgid,&contact,sizeof(struct Contact)-sizeof(long),0); 
        printf("联系人数据发送完毕\n");
    }
    
    else if(sig->signal == 2){
        //指令2:清空通讯录
        seq->n = 0;
        sig->type = 9;
        sig->signal = OK;
        msgsnd(msgid,sig,sizeof(int),0);
    }
}


//处理注册信息
int handle_regist(int msgid,void* data,struct Seq_users* seq){
    printf("已响应注册操作!\n");
    struct User *user = (struct User*)data; 

    printf("username:%s\npassword:%s\n",user->username,user->password);
    strcpy(seq->users[seq->n].username,user->username);
    strcpy(seq->users[seq->n].password,user->password);
    (seq->n)++;
    printf("n:%d\n",seq->n);
    //向前台发送OK(0)
    struct Msg_sig sig = {RECEIVE_SIG_INT,OK};
    msgsnd(msgid,&sig,sizeof(sig)-sizeof(long),0);

}

//处理登录的用户信息
int handle_login(int msgid,void* data,struct Seq_users* seq){
    printf("已响应登录操作\n");
    struct User *user = (struct User*)data; 
    printf("username:%s\npassword:%s\n",user->username,user->password);
    struct Msg_sig sig = {RECEIVE_SIG_INT,OK};
    //将登录信息与线性表里的数据做比较
    for(int i=0;i<(seq->n);i++){
        if(strcmp(user->username,seq->users[i].username)==0
           && strcmp(user->password,seq->users[i].password)==0){
           //发送OK(0)
           printf("登录信息正确!\n");
           int ret = msgsnd(msgid,&sig,sizeof(sig)-sizeof(long),0); 
           return 0;
        }
    }
    printf("登录信息错误!\n");
    sig.signal = NO;
    msgsnd(msgid,&sig,sizeof(int),0);
    return -1;
}

//处理添加用户
int handle_add(int msgid,void* data,struct Seq_contacts* seq){
    printf("已响应添加\n");
    struct Contact* contact = (struct Contact*)data;
    //将新用户数据暂存在线性表里
    int i = seq->n;
    strcpy(seq->contacts[i].name,contact->name);
    strcpy(seq->contacts[i].sex,contact->sex);
    strcpy(seq->contacts[i].address,contact->address);
    strcpy(seq->contacts[i].telephone,contact->telephone);
    seq->contacts[i].age = contact->age;
    seq->n++;
    //返回添加成功
    struct Msg_sig sig = {RECEIVE_SIG_INT,OK};
    msgsnd(msgid,&sig,sizeof(int),0);
    return 0;
}

//全局变量记录最近查询联系人的下标
int index_search = -1;

//处理查询
int handle_search(int msgid,void* data,const struct Seq_contacts* seq){
    printf("已响应查询\n");
    struct Contact* contact = (struct Contact*)data;
    int i = 0;
    for(i=0;i<seq->n;i++){
        if(strcmp(contact->name,seq->contacts[i].name) == 0){
            //查找到该联系人
            index_search = i;
            strcpy(contact->name,seq->contacts[i].name);
            strcpy(contact->sex,seq->contacts[i].sex);
            contact->age = seq->contacts[i].age;
            strcpy(contact->telephone,seq->contacts[i].telephone);
            strcpy(contact->address,seq->contacts[i].address);
            break;
        }
    }
    if(i>=(seq->n)){
        contact->age = -1;
        index_search = -1;
    }
    contact->type = RECEIVE_CONTACT;
    msgsnd(msgid,contact,sizeof(struct Contact)-sizeof(long),0);
}

//处理删除
int handle_remove(int msgid,void* data,struct Seq_contacts* seq){
    struct Contact* contact = (struct Contact*)data;
    //查找到该联系人,将姓名置为空
    strcpy(seq->contacts[index_search].name,"");
    struct Msg_sig sig = {RECEIVE_SIG_INT,OK};
    msgsnd(msgid,&sig,sizeof(int),0);
}

//处理更改
int handle_updata(int msgid,void* data,struct Seq_contacts* seq){
    struct Contact* contact = (struct Contact*)data;
    //更改信息
    strcpy(seq->contacts[index_search].name,contact->name);
    strcpy(seq->contacts[index_search].sex,contact->sex);
    seq->contacts[index_search].age = contact->age;
    strcpy(seq->contacts[index_search].telephone,contact->telephone);
    strcpy(seq->contacts[index_search].address,contact->address);
    struct Msg_sig sig = {RECEIVE_SIG_INT,OK};
    msgsnd(msgid,&sig,sizeof(int),0);
}

//将线性表信息写入文件
void w_file(struct Seq_users* p_users,struct Seq_contacts* p_contacts){
    FILE* fp_user = fopen("./sheet_user.txt","w");
    for(int i=0;i<p_users->n;i++){
        fputs(p_users->users[i].username,fp_user);
        fputc(' ',fp_user);
        fputs(p_users->users[i].password,fp_user);
        fputc('\n',fp_user);
    }
    fclose(fp_user);

    FILE* fp_contact = fopen("./sheet_contact.txt","w");
    for(int i=0;i<(p_contacts->n);i++){
        fputs("姓名:",fp_contact);
        fputs(p_contacts->contacts[i].name,fp_contact);
        fputc('\n',fp_contact);
        fputs("性别:",fp_contact);
        fputs(p_contacts->contacts[i].sex,fp_contact);
        fputc('\n',fp_contact);

        char age[4] = "";
        sprintf(age,"%d",p_contacts->contacts[i].age);
        fputs("年龄:",fp_contact);
        fputs(age,fp_contact);
        fputc('\n',fp_contact);
        fputs("电话号码:",fp_contact);
        fputs(p_contacts->contacts[i].telephone,fp_contact);
        fputc('\n',fp_contact);
        fputs("家庭住址:",fp_contact);
        fputs(p_contacts->contacts[i].address,fp_contact);
        fputc('\n',fp_contact);
    }
    fclose(fp_contact);
}

int main(int argc, char const *argv[])
{
    
    //1.生成key
    int key = ftok("./app2",'b');
    if(key<0){
        perror("ftok err");
        return -1;
    }
    //2.创建或打开消息队列
    int msgid = msgget(key,IPC_CREAT|IPC_EXCL|0666);
    if(msgid<0){
        //消息队列存在
        if(errno == EEXIST){
            msgid = msgget(key,0666);
        }
        else{
            perror("msgget err");
            return -1;
        }
    }
    printf("消息队列创建成功!\n");

    //读取用户信息文件
    FILE* fp_user = fopen("./sheet_user.txt","a+");
    if(fp_user == NULL){
        perror("fopen err");
        return -1;
    }
    fseek(fp_user,0,SEEK_SET);

    //创建存储用户信息的线性表
    struct Seq_users* p_users = malloc(sizeof(struct Seq_contacts));
    if(p_users == NULL){
        perror("malloc err");
        return -1;
    }
    memset(p_users,0,sizeof(p_users));

    for(int i=0; ;i++){
        char buf[100] = "";
        fgets(buf,sizeof(buf),fp_user);
        //如果文件读完
        if(buf[0] == '\0'){
            break;
        }
        //解析用户名和密码
        sscanf(buf,"%s %s",p_users->users[i].username,p_users->users[i].password);
        printf("username:%s\npassword:%s\n",p_users->users[i].username,p_users->users[i].password);
        p_users->n++;
    }
    //关闭用户信息文件
    printf("用户信息文件读取完毕!\n");
    fclose(fp_user);

    //读取通讯录
    FILE* fp_contact = fopen("./sheet_contact.txt","a+");
    if(fp_contact == NULL){
        perror("fopen err");
        return -1;
    }
    fseek(fp_contact,0,SEEK_SET);
    //创建通讯录线性表
    struct Seq_contacts* p_contacts = (struct Seq_contacts*)malloc(sizeof(struct Seq_contacts));
    if(p_contacts == NULL){
        perror("malloc err");
        return -1;
    }
    memset(p_contacts,0,sizeof(struct Seq_contacts));
    //将文件信息暂存入线性表
    while(1){
        char buf[100] = "";
        fgets(buf,sizeof(buf),fp_contact);
        if(feof(fp_contact) == 1){
            break;
        }
        sscanf(buf,"姓名:%s",p_contacts->contacts[p_contacts->n].name);

        fgets(buf,sizeof(buf),fp_contact);
        sscanf(buf,"性别:%s",p_contacts->contacts[p_contacts->n].sex);

        fgets(buf,sizeof(buf),fp_contact);
        sscanf(buf,"年龄:%d",&p_contacts->contacts[p_contacts->n].age);

        fgets(buf,sizeof(buf),fp_contact);
        sscanf(buf,"电话号码:%s",p_contacts->contacts[p_contacts->n].telephone);

        fgets(buf,sizeof(buf),fp_contact);
        sscanf(buf,"家庭住址:%s",p_contacts->contacts[p_contacts->n].address);
        p_contacts->n++;
    }

    //关闭联系人信息文件
    printf("通讯录信息文件读取完毕!\n");
    fclose(fp_contact);

    void* buf = malloc(sizeof(struct Contact));
    if(buf == NULL){
        perror("malloc err");
        return -1;
    }
    while(1){
        msgrcv(msgid,buf,sizeof(struct Contact)-sizeof(long),-8,MSG_NOERROR);
        long type = *(long*)buf;
        printf("type:%ld\n",type);
        switch (type)
        {
        case 1:
            //接受到指令
            if(handle_sigs(msgid,buf,p_contacts) == -1){
                w_file(p_users,p_contacts);
                type = -1;
            }
            break;
        case 2:
            //接受到用户注册信息
            handle_regist(msgid,buf,p_users);
            break;
        case 3:
            //接受到用户登录信息
            handle_login(msgid,buf,p_users);
            break;
        case 4:
            //接受到需添加的联系人信息
            handle_add(msgid,buf,p_contacts);
            break;
        case 5:
            //接受到查询的联系人信息(姓名)
            handle_search(msgid,buf,p_contacts);
            break;
        case 6:
            //接受到删除的联系人信息(姓名)
            handle_remove(msgid,buf,p_contacts);
            break;
        case 7:
            //接受到新的联系人信息
            handle_updata(msgid,buf,p_contacts);
        default:
            break;
        }
        if(type == -1){
            free(buf);
            buf = NULL;
            //删除消息队列,释放线性表的内存
            msgctl(msgid,IPC_RMID,NULL);
            free(p_contacts);
            p_contacts = NULL;
            free(p_users);
            p_users = NULL;
            break;
        }
    }
    return 0;
}
